/**
 * Copyright &copy; 2012-2016 <a href="https://istep.lczyfz.com">Istep</a> All rights reserved.
 */
package com.lczyfz.istep.modules.sys.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.lczyfz.istep.common.enums.JobIsFactoryType;
import com.lczyfz.istep.common.enums.JobStatus;
import com.lczyfz.istep.common.utils.SpringContextHolder;
import com.lczyfz.istep.common.utils.quartz.QuartzJobFactory;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.lczyfz.istep.common.persistence.Page;
import com.lczyfz.istep.common.service.CrudService;
import com.lczyfz.istep.modules.sys.entity.ScheduleJob;
import com.lczyfz.istep.modules.sys.dao.ScheduleJobDao;

import javax.annotation.PostConstruct;

/**
 * 任务调度JobService
 *
 * @author maple
 * @version 2016-12-14
 */
@Service
@Transactional(readOnly = true)
public class ScheduleJobService extends CrudService<ScheduleJobDao, ScheduleJob> {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleJobService.class);
    public static final String JOB_GROUP_ORDER = "order";
    public static final String JOB_GROUP_ORDER_AUTO_CONFIRM = "order_auto_confirm";
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    public ScheduleJob get(String id) {
        return super.get(id);
    }

    public List<ScheduleJob> findList(ScheduleJob scheduleJob) {
        return super.findList(scheduleJob);
    }

    public Page<ScheduleJob> findPage(Page<ScheduleJob> page, ScheduleJob scheduleJob) {
        return super.findPage(page, scheduleJob);
    }

    @Transactional(readOnly = false)
    public void save(ScheduleJob scheduleJob) {
        super.save(scheduleJob);
    }

    @Transactional(readOnly = false)
    public void delete(ScheduleJob scheduleJob) {
        super.delete(scheduleJob);
    }

    public ScheduleJob findByJobName(String jobGroup, String jobName) {
        return dao.findByJobName(jobGroup, jobName);
    }
    @PostConstruct
    public void init() throws Exception {
        // 这里获取未删除、运行状态的任务信息数据
        //TODO:服务器跪了，任务执行时间已过，待处理
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setStatus(JobStatus.Running.getValue());
        scheduleJob.setDelFlag("0");
        List<ScheduleJob> jobList = getAllTask(scheduleJob);
        for (ScheduleJob job : jobList) {
            isOverdue(job);
            if (job.getIsFactory().equals(JobIsFactoryType.Factory.getValue())) {
                addJobWithFactory(job);
            } else if (job.getIsFactory().equals(JobIsFactoryType.Common.getValue())) {
                addJob(job);
            } else {
                addJob(job);
            }
            super.save(job);

        }
    }

    /**
     * 〈方法的功能描述〉:
     * 判断任务是否过时，过时则重新设置执行时间
     *
     * @param
     * @return
     * @Date 2016/12/8/16:02
     * @author zy
     * @methodName
     **/

    public void isOverdue(ScheduleJob scheduleJob) {
        if (scheduleJob.getStartDate() != null) {
            if (scheduleJob.getStartDate().getTime() - new Date().getTime() <= 0) {
                scheduleJob.setCronExpression(getTime());
            }
        }


    }

    /**
     * 〈方法的功能描述〉:
     * 获取过时任务新的执行时间
     *
     * @param
     * @return
     * @Date 2016/12/8/16:03
     * @author zy
     * @methodName
     **/

    public String getTime() {
        java.util.Calendar ca = java.util.Calendar.getInstance();//得到一个Calendar的实例
        ca.setTime(new Date()); //设置时间为当前时间
        ca.add(java.util.Calendar.MINUTE, +1);//防止任务过多再次过时所以加一分钟
        int year = ca.get(java.util.Calendar.YEAR);//获取年份
        int month = ca.get(java.util.Calendar.MONTH) + 1;//获取月份
        int day = ca.get(java.util.Calendar.DATE);//获取日
        int hour = ca.get(java.util.Calendar.HOUR_OF_DAY);//小时
        int minute = ca.get(java.util.Calendar.MINUTE);//分
        int second = ca.get(java.util.Calendar.SECOND);//秒
        //"30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务
        String time = second + " " + minute + " " + hour + " " + day + " " + month + " " + "? " + year;
        return time;

    }

    /**
     * 从数据库中取 区别于getAllJob
     *
     * @return
     */
    public List<ScheduleJob> getAllTask(ScheduleJob scheduleJob) {
        return super.findList(scheduleJob);
    }

    /**
     * 添加到数据库中 区别于addJob
     */
    @Transactional(readOnly = false)
    public void addTask(ScheduleJob scheduleJob) throws SchedulerException {

        //todo:需要登录情况下才能添加成功
        super.save(scheduleJob);
        if (scheduleJob.getIsFactory().equals(JobIsFactoryType.Factory.getValue())) {
            addJobWithFactory(scheduleJob);
        } else if (scheduleJob.getIsFactory().equals(JobIsFactoryType.Common.getValue())) {
            addJob(scheduleJob);
        } else {
            addJob(scheduleJob);
        }
    }

    /**
     * 从数据库中查询job
     */
    public ScheduleJob getTaskById(String id) {
        return super.get(id);
    }

    /**
     * 更改任务状态
     *
     * @throws SchedulerException
     */
    public void changeJobStatus(String jobId, String cmd) throws SchedulerException {
        ScheduleJob job = getTaskById(jobId);
        if (job == null) {
            return;
        }
        if ("stop".equals(cmd)) {
            deleteJob(job);
            job.setStatus(JobStatus.Stopped.getValue());
        } else if ("start".equals(cmd)) {
            job.setStatus(JobStatus.Running.getValue());
            addJob(job);
        }
        save(job);
    }

    /**
     * 更改任务 cron表达式
     *
     * @throws SchedulerException
     */
    public void updateTaskCron(String jobId, String cron) throws SchedulerException {
        ScheduleJob job = getTaskById(jobId);
        if (job == null) {
            return;
        }
        job.setCronExpression(cron);
        if (JobStatus.Running.getValue().equals(job.getStatus())) {
            updateJobCron(job);
        }
        save(job);

    }

    /**
     * 更新job时间表达式
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        scheduler.rescheduleJob(triggerKey, trigger);
    }

    /**
     * 添加任务
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void addJob(ScheduleJob scheduleJob) throws SchedulerException {
        if (scheduleJob == null || !JobStatus.Running.getValue().equals(scheduleJob.getStatus()) || scheduleJob.getDelFlag().equals("1")) {
            LOGGER.error("ScheduleJob null or Stop or Delete");
            return;
        }
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (null == trigger) {
            Class clazz = null;
            if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
                try {
                    clazz = Class.forName(scheduleJob.getBeanClass());
                } catch (ClassNotFoundException e) {
                    LOGGER.error("ScheduleJob initialize error, ClassNotFoundException: {}", scheduleJob.getBeanClass());
                }
            } else if (StringUtils.isNotBlank(scheduleJob.getBeanName())) {
                clazz = SpringContextHolder.getType(scheduleJob.getBeanName());

            }
            @SuppressWarnings("unchecked")
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
            jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
            // 按新的cronExpression表达式构建一个新的trigger
            trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
            Date date = scheduler.scheduleJob(jobDetail, trigger);
            LOGGER.debug(scheduleJob.getJobName() + " run at " + date + " and repeat on expression " + trigger.getCronExpression());
        } else {
            // Trigger已存在，那么更新相应的定时设置
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            Date date = scheduler.rescheduleJob(triggerKey, trigger);
            LOGGER.debug(scheduleJob.getJobName() + "reset, run at " + date + " and repeat on expression " + trigger.getCronExpression());

        }
    }

    /**
     * 添加任务 通过Factory
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void addJobWithFactory(ScheduleJob scheduleJob) throws SchedulerException {
        if (scheduleJob == null || !JobStatus.Running.getValue().equals(scheduleJob.getStatus())) {
            LOGGER.error("ScheduleJob null or Stop");
            return;
        }
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (null == trigger) {
            Class clazz = QuartzJobFactory.class;
            @SuppressWarnings("unchecked")
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
            jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
            // 按新的cronExpression表达式构建一个新的trigger
            trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
            Date date = scheduler.scheduleJob(jobDetail, trigger);
            LOGGER.debug(scheduleJob.getJobName() + " run at " + date + " and repeat on expression " + trigger.getCronExpression());
        } else {
            // Trigger已存在，那么更新相应的定时设置
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            Date date = scheduler.rescheduleJob(triggerKey, trigger);
            LOGGER.debug(scheduleJob.getJobName() + "reset, run at " + date + " and repeat on expression " + trigger.getCronExpression());

        }
    }

    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    public List<ScheduleJob> getRunningJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            ScheduleJob job = new ScheduleJob();
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            job.setDescription("触发器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            // Todo: 检查一下这边是否设置正确
            job.setStatus(triggerState.name());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCronExpression(cronExpression);
            }
            jobList.add(job);
        }
        return jobList;
    }

    /**
     * 暂停一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.deleteJob(jobKey);
    }

    public void doJobTest(ScheduleJob scheduleJob) {

        System.out.println("Hello World ! - " + scheduleJob.getJobGroup() + "." + scheduleJob.getJobName() + " " + new Date());


    }
    @Transactional(readOnly = false)
    public void deleteScheduleJobDao(ScheduleJob scheduleJob)
    {
        dao.deleteScheduleJobDao(scheduleJob);
    }

}